package com.ruoyi.business.communication;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import com.ruoyi.business.communication.domain.PositionCountPerSlot;
import com.ruoyi.business.communication.domain.SlotSetting;
import com.ruoyi.business.communication.domain.SlotStatus;
import com.ruoyi.business.communication.domain.SlotTiming;
import com.ruoyi.business.device.domain.Device;
import com.ruoyi.business.modbus.domain.ModbusMasterDevice;
import com.ruoyi.business.modbus.domain.ModbusTcpMasterDevice;
import com.ruoyi.business.modbus.domain.ModbusReadResponse;
import com.ruoyi.business.modbus.domain.ModbusWriteResponse;
import com.ruoyi.business.modbus.service.IModbusCoreService;

import java.util.List;
import java.util.Map;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 设备通讯服务
 * <p>
 * - 设备信息从数据库读取（Device），无需本地静态配置
 * - 按设备类型(TCP/RTU)动态构建 Modbus 主站配置对象
 * - 路由到底层 IModbusCoreService 实现进行读写/测试/统计
 * - 提供与槽/点相关的常用换算（基于 Device 的字段）
 */
@Service
public class DeviceCommunicationService {

    private static final Logger logger = LoggerFactory.getLogger(DeviceCommunicationService.class);

    @Autowired
    @Qualifier("modbus4jTcpService")
    private IModbusCoreService modbusTcpService;

    private static Map<String, ModbusTcpMasterDevice> modbusDeviceCache = new ConcurrentHashMap<>();

    // 目前仅支持TCP
    // 读取槽状态
    public List<SlotStatus> readSlotStatus(Device device) {
        //返回值
        List<SlotStatus> slotStatusList = new ArrayList<>();
        final int WORD_LENGTH = 16;
        IModbusCoreService core = selectCoreService(device);
        ModbusMasterDevice modbusMasterDevice = buildModbusMasterDevice(device);
        int slave = parseSlaveAddress(device);
        //业务读取：线圈(0x01)
        ModbusReadResponse<Boolean> response = core.readCoils(
            modbusMasterDevice, 
            slave, 
            0, // 起始地址,从0开始，详见协议文档
            device.getSlotCount()*WORD_LENGTH
        );
        
        if (response.isSuccess()) {
            List<Boolean> values = response.getData();
            //将List根据槽的数量 分组
            List<List<Boolean>> groupedValues = new ArrayList<>();
            for (int i = 0; i < values.size(); i += WORD_LENGTH) {
                groupedValues.add(values.subList(i, i + WORD_LENGTH));
            }
            //将groupedValues转换为SlotStatus
            for (int slotIndex = 0; slotIndex < groupedValues.size(); slotIndex++) {
                List<Boolean> group = groupedValues.get(slotIndex);
                SlotStatus slotStatus = mapBitsToSlotStatus(device, slotIndex + 1, group);
                slotStatusList.add(slotStatus);
            }
        }
        
        return slotStatusList;
    }

    /**
     * 写入槽设置信息到指定设备的指定业务槽号
     * @param device 设备对象
     * @param businessSlotNo 槽的业务编码（业务槽号）
     * @param slotSetting 槽的设置信息
     * @param verify 是否写入后校验
     * @return 写入结果
     */
    public ModbusWriteResponse writeSlotSetting(Device device, 
            SlotSetting slotSetting, boolean verify) {
        if (device == null) {
            throw new IllegalArgumentException("设备对象不能为空");
        }
        if (slotSetting == null) {
            throw new IllegalArgumentException("槽设置信息不能为空");
        }
        // 计算本地槽索引（从1开始）
        int localSlotIndex = toLocalSlotIndex(device, slotSetting.getBusinessSlotNo());
        
        // 获取要写入的值（16位无符号整型）
        int value = slotSetting.toUint16();
        logger.debug("写入槽设置信息到指定设备的指定业务槽号，businessSlotNo: {}, slotSetting: {}", slotSetting.getBusinessSlotNo(), slotSetting);
        logger.debug("写入槽设置信息到指定设备的指定业务槽号，value: {}", value);
        // 选择核心服务
        IModbusCoreService core = selectCoreService(device);
        ModbusMasterDevice modbusMasterDevice = buildModbusMasterDevice(device);
        int slave = parseSlaveAddress(device);
        // 写入单寄存器 function code: 0x06
        return core.writeSingleRegister(modbusMasterDevice, slave, localSlotIndex, value, verify);
    }

    /**
     * 读取指定设备所有槽的点位时间设置（使用0x03功能码，按字节读取）
     * @param device 设备对象
     * @return 每个槽的SlotTiming对象列表
     */
    /**
     * 读取指定设备指定业务槽号的点位时间设置（每次只读一个槽）
     * @param device 设备对象
     * @param businessSlotNo 业务槽号
     * @return 该槽的SlotTiming对象列表（仅包含一个元素，未读到则为空）
     */
    public List<SlotTiming> readSlotTimings(Device device, Integer businessSlotNo) {
        if (device == null) {
            throw new IllegalArgumentException("设备对象不能为空");
        }
        if (businessSlotNo == null) {
            throw new IllegalArgumentException("业务槽号不能为空");
        }
        int pointsPerSlot = nvl(device.getPointsPerSlot());

        // 计算本地槽索引（从1开始）
        int localSlotIndex = toLocalSlotIndex(device, businessSlotNo);

        // 选择核心服务
        IModbusCoreService core = selectCoreService(device);
        ModbusMasterDevice modbusMasterDevice = buildModbusMasterDevice(device);
        int slave = parseSlaveAddress(device);

        // 计算该槽的读取的寄存器数量
        // 每个槽包含：打壳时间(pointsPerSlot字节) + 阶段1时间(pointsPerSlot字节) + 阶段2时间(pointsPerSlot字节)
        // 总共：pointsPerSlot * 3 字节
        // 转换为寄存器：一个字=2字节，所以是 (pointsPerSlot * 3 + 1) / 2 向上取整
        // 例如：4点位需要12字节=6寄存器，6点位需要18字节=9寄存器
        int totalRegisters = (pointsPerSlot * 3 + 1) / 2; 

        // 记录开始时间
        long startTime = System.currentTimeMillis();
        logger.debug("开始读取槽位时间设置：业务槽号={}, 本地槽索引={}, 寄存器数量={}", 
                businessSlotNo, localSlotIndex, totalRegisters);

        // 读取该槽的点位时间设置
        ModbusReadResponse<Integer> response = core.readHoldingRegisters(
                modbusMasterDevice, slave, localSlotIndex, totalRegisters);
        
        // 计算耗时
        long elapsedTime = System.currentTimeMillis() - startTime;
        logger.debug("读取槽位时间设置完成：业务槽号={}, 寄存器数量={}, 耗时={}ms, 成功={}", 
                businessSlotNo, totalRegisters, elapsedTime, response.isSuccess());

        List<SlotTiming> slotTimingList = new ArrayList<>();
        if (response.isSuccess()) {
            List<Integer> registerValues = response.getData();
            // 将寄存器数据拆分为字节数组
            byte[] allBytes = new byte[registerValues.size() * 2];
            for (int i = 0; i < registerValues.size(); i++) {
                int reg = registerValues.get(i);
                allBytes[i * 2] = (byte) ((reg >> 8) & 0xFF);     // 高字节
                allBytes[i * 2 + 1] = (byte) (reg & 0xFF);        // 低字节
            }
            // 只处理一个槽
            SlotTiming slotTiming = new SlotTiming(
                    pointsPerSlot == 4 ? PositionCountPerSlot.FOUR : PositionCountPerSlot.SIX,
                    businessSlotNo
            );
            // 打壳时间
            for (int i = 0; i < pointsPerSlot; i++) {
                int raw = allBytes[i] & 0xFF; // 1字节无符号
                slotTiming.setStrikeTimeRaw(i + 1, raw);
            }
            // 阶段1/2
            int stageBase = pointsPerSlot;
            for (int i = 0; i < pointsPerSlot; i++) {
                int stage1 = allBytes[stageBase + i * 2] & 0xFF;
                int stage2 = allBytes[stageBase + i * 2 + 1] & 0xFF;
                slotTiming.setStage1Minutes(i + 1, stage1);
                slotTiming.setStage2Minutes(i + 1, stage2);
            }
            slotTimingList.add(slotTiming);
        }
        return slotTimingList;
    }


    /** 设备总点位数（来自数据库字段） */
    public int getTotalPoints(Device device) {
        return nvl(device.getSlotCount()) * nvl(device.getPointsPerSlot());
    }

    /** 本地槽索引(从1开始) -> 业务槽号：offset + local */
    public int toBusinessSlotNo(Device device,int localSlotIndex) {
        int total = nvl(device.getSlotCount());
        if (localSlotIndex < 1 || localSlotIndex > total) {
            throw new IllegalArgumentException("本地槽索引应在1.." + total);
        }
        return nvl(device.getSlotOffset()) + localSlotIndex;
    }

    /** 业务槽号 -> 本地槽索引(从1开始) */
    public int toLocalSlotIndex(Device device, int businessSlotNo) {
        int start = nvl(device.getStartSlotNumber());
        int end = nvl(device.getEndSlotNumber());
        if (businessSlotNo < start || businessSlotNo > end) {
            throw new IllegalArgumentException("业务槽号超出设备范围: " + businessSlotNo);
        }
        return businessSlotNo - nvl(device.getSlotOffset());
    }

    /**
     * 将位序映射为SlotStatus对象
     * 位序定义（从高位到低位）：
     * 0: reserved2, 1: reserved1, 
     * 2-7: discharge6-1, 
     * 8-9: doorPowerBoxOpen/doorControllerOpen, 
     * 10-15: jam6-1
     */
    private SlotStatus mapBitsToSlotStatus(Device device, int localSlotIndex, List<Boolean> bits) {
        if (bits.size() < 16) {
            throw new IllegalArgumentException("位序数据不足16位，当前: " + bits.size());
        }
        
        SlotStatus slotStatus = new SlotStatus();
        slotStatus.setBusinessSlotNo(toBusinessSlotNo(device, localSlotIndex));
        
        // 设置点位数量枚举
        if (device.getPointsPerSlot() == 4) {
            slotStatus.setPositionCountPerSlot(PositionCountPerSlot.FOUR);
        } else if (device.getPointsPerSlot() == 6) {
            slotStatus.setPositionCountPerSlot(PositionCountPerSlot.SIX);
        }
        
        // 位序映射（按协议文档定义）
        slotStatus.setReserved2(bits.get(0));      // 位0
        slotStatus.setReserved1(bits.get(1));      // 位1
        slotStatus.setDischarge6(bits.get(2));     // 位2
        slotStatus.setDischarge5(bits.get(3));     // 位3
        slotStatus.setDischarge4(bits.get(4));     // 位4
        slotStatus.setDischarge3(bits.get(5));     // 位5
        slotStatus.setDischarge2(bits.get(6));     // 位6
        slotStatus.setDischarge1(bits.get(7));     // 位7
        slotStatus.setDoorPowerBoxOpen(bits.get(8));    // 位8
        slotStatus.setDoorControllerOpen(bits.get(9));  // 位9
        slotStatus.setJam6(bits.get(10));          // 位10
        slotStatus.setJam5(bits.get(11));          // 位11
        slotStatus.setJam4(bits.get(12));          // 位12
        slotStatus.setJam3(bits.get(13));          // 位13
        slotStatus.setJam2(bits.get(14));          // 位14
        slotStatus.setJam1(bits.get(15));          // 位15
        
        return slotStatus;
    }


    private IModbusCoreService selectCoreService(Device device) {
        String type = device.getDeviceType();
        if (type == null) {
            throw new IllegalArgumentException("设备类型为空");
        }
        if ("TCP".equalsIgnoreCase(type)) {
            return modbusTcpService;
        }
        throw new IllegalArgumentException("当前仅支持TCP设备，收到类型: " + type);
    }

    private int parseSlaveAddress(Device device) {
        String relay = device.getDeviceRelayCode();
        if (relay == null || relay.trim().isEmpty()) {
            throw new IllegalArgumentException("设备中继编码(从站地址)为空");
        }
        try {
            return Integer.parseInt(relay.trim());
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException("设备中继编码(从站地址)格式错误: " + relay);
        }
    }
    
    private ModbusMasterDevice buildModbusMasterDevice(Device device) {
        //将device的deviceRelayCode和ModbusTcpMasterDevice对象进行缓存，不需要每次都创建新的
        // 使用缓存避免重复创建ModbusTcpMasterDevice对象
        // 以deviceRelayCode为key进行缓存
        if (device == null || device.getDeviceRelayCode() == null) {
            throw new IllegalArgumentException("设备或设备中继编码(从站地址)为空");
        }
        // 简单线程安全缓存实现
        // 注意：如需跨服务/分布式环境请用更高级缓存
        String relayCode = device.getDeviceRelayCode();
        ModbusTcpMasterDevice cached = modbusDeviceCache.get(relayCode);
        if (cached != null) {
            // 更新设备名称、IP、端口等动态属性，防止设备信息变更后缓存过时
            cached.setDeviceName(device.getDeviceName());
            cached.setIpAddress(device.getIpAddress());
            cached.setPort(device.getPort());
            modbusDeviceCache.put(device.getDeviceRelayCode(), cached);
            return cached;
        }
        String type = device.getDeviceType();
        if ("TCP".equalsIgnoreCase(type)) {
            ModbusTcpMasterDevice modbusTcpMasterDevice = new ModbusTcpMasterDevice();
            modbusTcpMasterDevice.setDeviceSN(String.valueOf(device.getDeviceRelayCode()));
            modbusTcpMasterDevice.setDeviceName(device.getDeviceName());
            modbusTcpMasterDevice.setIpAddress(device.getIpAddress());
            modbusTcpMasterDevice.setPort(device.getPort());

            modbusDeviceCache.put(device.getDeviceRelayCode(), modbusTcpMasterDevice);
            return modbusTcpMasterDevice;
        }
        throw new IllegalArgumentException("当前仅支持TCP设备，收到类型: " + type);
    }

    private int nvl(Integer v) { return v == null ? 0 : v; }
}


